﻿using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AugustUI
{
    public class CV
    {
        public static void CV_Imread(string filename, out Mat matout, ImreadModes imreadModes)
        {
            matout = CvInvoke.Imread(filename, imreadModes);
            //imagePanel.DisplayImage = matout;
        }


        public static void CV_Threshold(Mat mat, int threshold, int maxthreshold, Emgu.CV.CvEnum.ThresholdType thresholdType, ref Mat matout)
        {
            Mat image = mat;
            double k = CvInvoke.Threshold(image, matout, 0, 255, thresholdType);
            //imagePanel.DisplayImage = matout;
        }

        public static void CV_BitwiseNot(Mat mat, ref Mat matout)
        {
            Mat image = mat;
            CvInvoke.BitwiseNot(image, matout);
            //imagePanel.DisplayImage = matout;
        }


        public static void CV_Erode(Mat mat, int radius, ref Mat matout)
        {
            Mat element = CvInvoke.GetStructuringElement(Emgu.CV.CvEnum.ElementShape.Rectangle, new Size(radius, radius), new Point(-1, -1));
            CvInvoke.Erode(mat, matout, element, new Point(-1, -1), 1, Emgu.CV.CvEnum.BorderType.Default, new Emgu.CV.Structure.MCvScalar());
            //imagePanel.DisplayImage = matout;
        }

        public static void CV_CalcHist(Mat mat)
        {
            VectorOfMat vMatImg1 = new VectorOfMat();
            VectorOfPoint vectorOfPoint = new VectorOfPoint();
            int[] channels_1 = new int[] { 0 };
            Mat dHist_1 = new Mat();
            int[] histSize_1 = new int[] { 256 };       //bins(柱子个数)
            float[] range_1 = new float[] { 0, 256 };   //每一维数值的取值范围
            vMatImg1.Push(mat);
            CvInvoke.CalcHist(vMatImg1, channels_1, new Mat(), dHist_1, histSize_1, range_1, false);
        }


        public static void CV_FindRegion(Mat mat, out VectorOfVectorOfPoint vvp, out VectorOfPoint maxvp, ref Mat matout)
        {
            vvp = new VectorOfVectorOfPoint();
            Image<Bgr, Byte> disp = new Image<Bgr, byte>(mat.Width, mat.Height);
            CvInvoke.FindContours(mat, vvp, new Mat(), Emgu.CV.CvEnum.RetrType.Ccomp, Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxNone);
            double max = 0d;
            int maxindex = 0;
            for (int i = 0; i < vvp.Size; i++)
            {
                double area = CvInvoke.ContourArea(vvp[i]);
                if (area > max)
                {
                    max = area;
                    maxindex = i;
                }
            }
            maxvp = vvp[maxindex];
            CvInvoke.DrawContours(disp, vvp, maxindex, new MCvScalar(0, 0, 255), -1);
            matout = disp.Mat;
            //imagePanel.DisplayImage = matout;
        }


        public static void CV_MinEnclosingCircle(VectorOfPoint vp, out CircleF circleF)
        {
            //Image<Bgr, byte> ORimage = new Image<Bgr, byte>(mat.Bitmap);
            circleF = CvInvoke.MinEnclosingCircle(vp);
            //CvInvoke.Circle(ORimage, new Point((int)circleF.Center.X, (int)circleF.Center.Y), (int)circleF.Radius, new MCvScalar(0, 0, 255), 1);
            //matout = ORimage.Mat;
            //imagePanel.DisplayImage = matout;
        }


        public static bool CV_Calib_FindChessboardCorners(Mat mat, Size size, out PointF[] pointF)
        {
            using (VectorOfPointF vectorOfPoint = new VectorOfPointF())
            {
                bool ret = CvInvoke.FindChessboardCornersSB(mat, size, vectorOfPoint, CalibCbType.Exhaustive | CalibCbType.Accuracy);
                pointF = vectorOfPoint.ToArray();
                return ret;
            }
        }

        public static Mat CV_Calib_FindHomography2D(PointF[] from, PointF[] to, out double rms)
        {
            Mat homMat2D = CvInvoke.FindHomography(from, to);
            PointF[] pointFsTo = CvInvoke.PerspectiveTransform(from, homMat2D);
            double[] offsets = new double[pointFsTo.Length];
            offsets = GetDistance(pointFsTo, to);
            double avg = offsets.Average();
            rms = offsets.Sum(x => Math.Pow(x - avg, 2)) / offsets.Length;
            return homMat2D;
        }

        private static double[] GetDistance(PointF[] pointA, PointF[] pointB)
        {
            if (pointA.Length != pointB.Length) throw new Exception("getDistance : point length not equal");
            double[] distances = new double[pointA.Length];
            for (int i = 0; i < distances.Length; i++)
            {
                distances[i] = Math.Pow((pointA[i].X - pointB[i].X), 2) + Math.Pow((pointA[i].Y - pointB[i].Y), 2);
                distances[i] = Math.Sqrt(distances[i]);
            }
            return distances;
        }


        public static PointF[] CV_Calib_TransformHomoGraphy2D(PointF[] pointFs, Mat mat)
        {
            return CvInvoke.PerspectiveTransform(pointFs, mat);
        }

        public static PointF[] CV_Calib_GetCalibBoardPoints(Size size, float width, float height, PointF originPoint)
        {
            PointF[] pointFs = new PointF[size.Width * size.Height];
            for (int i = 0; i < size.Height; i++)
            {

                for (int j = 0; j < size.Width; j++)
                {

                    int index = i * size.Width + j;
                    pointFs[index].Y = originPoint.Y + i * height;
                    pointFs[index].X = originPoint.X + j * width;
                }
            }
            return pointFs;
        }

        public static void Dev_GenCircleXld(float centerX, float centerY, float radius, out Circle circleXld)
        {
            circleXld = new Circle(centerX, centerY, radius);
        }

        public static void Dev_GenLineXld(PointF[] pointFs, out Line lineXld)
        {
            lineXld = new Line(pointFs);
        }

        public static void Dev_GenCrossXld(PointF[] pointFs, float radius, float angle, out Cross crossXld)
        {
            crossXld = new Cross(pointFs, radius, angle);
        }


    }
}
